#ifndef ERUNNER_H
#define ERUNNER_H

#include "node.h"
#include "lane.h"
#include "port.h"
#include "channel.h"

#include "ip/fabrics.h"

#include "ip/opdef.h"

typedef struct LaneChannelERunnerSoftmemReadPack
{
    int dst_pid;
    bool sb;
    int dst_uri;
    int r_addr_0;
    bool r_addr_0_wait;
    bool r_ain_0_wait;
    int r_addr_1;
    bool r_addr_1_wait;
    bool r_ain_1_wait;
    AluOpCode op_code;
} LaneChannelERunnerSoftmemReadPack;

class LaneChannelERunnerSoftmemReadReceiver : public LanePortReceiver
{
public:
    PortIn<int> * dst_uri;
    PortIn<bool> * sb;
    PortIn<int> * r_addr_0;
    PortIn<bool> * r_addr_0_wait;
    PortIn<bool> * r_ain_0_wait;
    PortIn<int> * r_addr_1;
    PortIn<bool> * r_addr_1_wait;
    PortIn<bool> * r_ain_1_wait;
    PortIn<AluOpCode> * op_code;
    PortIn<int> * dst_pid;

    LaneChannelERunnerSoftmemReadReceiver(JObject *parent = nullptr);
    LaneChannelERunnerSoftmemReadPack get();
    bool is_any_empty();
    bool is_any_full();
};

class LaneChannelERunnerSoftmemReadSender : public LanePortSender
{
public:
    PortOut<int> * dst_uri;
    PortOut<bool> * sb;
    PortOut<int> * r_addr_0;
    PortOut<bool> * r_addr_0_wait;
    PortOut<bool> * r_ain_0_wait;
    PortOut<int> * r_addr_1;
    PortOut<bool> * r_addr_1_wait;
    PortOut<bool> * r_ain_1_wait;
    PortOut<AluOpCode> * op_code;
    PortOut<int> * dst_pid;

    LaneChannelERunnerSoftmemReadSender(JObject *parent = nullptr);
    void connect(LaneChannelERunnerSoftmemReadReceiver *sender);
    void put(int data_dst_uri, bool data_sb, int data_r_addr_0, bool data_r_addr_0_wait, bool data_r_ain_0_wait, int data_r_addr_1, bool data_r_addr_1_wait, bool data_r_ain_1_wait, AluOpCode data_op_code, int data_dst_pid);
    void put_bypass(LaneChannelERunnerSoftmemReadReceiver *receiver);
    bool is_any_full();
};

class ChannelERunnerSoftmemReceiver : public ChannelPortReceiver
{
public:
    LaneChannelERunnerSoftmemReadReceiver * Read;

    ChannelERunnerSoftmemReceiver(JObject *parent);
};

class ChannelERunnerSoftmemSender : public ChannelPortSender
{
public:
    LaneChannelERunnerSoftmemReadSender * Read;

    ChannelERunnerSoftmemSender(JObject *parent);
};

class ChannelERunnerSoftmem : public Channel
{
public:
    static void connect(ChannelERunnerSoftmemSender *sender, ChannelERunnerSoftmemReceiver *receiver);
};

typedef struct NodeERunnerParam
{
    int num_of_alu;
    int peer_id;
    int total_peers;
    List<int> base_ptrs;
} NodeERunnerParam;

class NodeERunner : public Node
{
public:
    int const_num_of_alu;
    int const_peer_id;
    int const_total_peers;
    List<int> const_base_ptrs;
    bool reg_is_running;
    int reg_completed_alu;
    int reg_nano_op_count;
    int reg_alu_arbiter;
    List<bool> reg_alu_enable;
    List<int> reg_alu_dst;
    List<int> reg_alu_src_addr0;
    List<int> reg_alu_src_addr1;
    List<int> reg_alu_len_count;
    ExecOp reg_nano_op;
    int reg_peer_count;
    int reg_alu_count;
    int reg_alu_mask;
    int reg_alu_offset;
    int reg_total_alu_offset;
    ChannelExecOpFabricReceiver * port_nano_op_receiver;
    List<ChannelERunnerSoftmemSender *> port_softmem_senders;

    NodeERunner(Node *parent, NodeERunnerParam params);
    NodeTaskList tasks() const override;

    void task_dispatch_execution();
    void task_fetch_op();
};

#endif // ERUNNER_H